#ifndef __IM_UNICODE_H__
#define __IM_UNICODE_H__

#include <im_base_types.h>

/**
 * @defgroup IMUnicode
 * @ingroup PublicIME
 * @{
 */

IM_BEGIN_DECLS

#define IM_UNICHAR_INVALID      ((IMUniChar)(-1))
#define IM_UNICHAR_REPLACEMENT  ((IMUniChar)0xFFFD)

/* Array of skip-bytes-per-initial character. */
extern const IMChar * const im_utf8_skip;

#define im_utf8_char_length(p) (IMSize)(im_utf8_skip [*(IMUChar *)(p)])
#define im_utf8_next_char(p) (IMChar *)((p) + im_utf8_char_length(p))

/**
 * @brief Get an unicode char from an utf8 char sequence.
 *
 * The im_utf8_get_char() function shall convert  the first character from utf8 sequences that src points to,
 * to a ucs4 character.
 *
 * @return The im_utf8_get_char() function shall return ucs4 character correspoinding to the src argument.
 * If src is a null pointer or the character sequence pointed by src does not form valid utf8 string,
 * im_utf8_get_char() function shall return IM_UNICODE_CHAR_INVALID.
 */
IMUniChar       im_utf8_get_char            (const IMChar           *src);

/**
 * @brief Return how many unicode chars in an utf8 string.
 *
 * The im_utf8_strlen() function shall compute the number of characters at most maxlen bytes in the
 * utf8 string to src points, not including the terminating null byte.
 *
 * If src is longer than maxlen bytes and maxlen has positive value, only first maxlen bytes are used to compute the number of characters.
 * If maxlen indicates -1, whole utf8 string to src points are computed.
 *
 * In the case of src doesn't points utf8 string, the behavior is unspecified.
 *
 * @param src a pointer to the utf8 string to be computed.
 * @param maxlen bytes used to compute the number of characters.
 * @returns The im_utf8_strlen() function shall return the length of src.
 */
IMSize          im_utf8_strlen              (const IMChar           *src,
                                             IMInt                   maxlen);

/**
 * @brief validate a utf8 string.
 *
 * The im_utf8_string_validate() function tests for any characters encoded with utf8 at most maxlen bytes in the
 * utf8  string to src points.
 *
 * If src is longer than maxlen bytes and maxlen has positive value, only first maxlen bytes are used to test .
 * If maxlen indicates -1, whole string to src points are tested.
 *
 * If the str contains illegal utf8 characters and end is not a null pointer, A pointer to the first illegal character is stored 
 * in the object pointed to by end.
 *
 * @param str the pointer to the utf8 string to be tested
 * @param maxlen Maximum length of str to be tested
 * @returns The im_utf8_string_validate() function shall return TRUE If character sequences pointed to by str form a valid character in utf8 codeset.
 */
IMBool          im_utf8_string_validate     (const IMChar           *str,
                                             IMInt                   maxlen,
                                             const IMChar          **end);

#define im_utf16_char_length(p)             \
    ((p) ?                                  \
     (((*(IMUInt16*)(p)) >= 0xd800 &&       \
       (*(IMUInt16*)(p)) < 0xdc00 &&        \
       (*(IMUInt16*)((p)+1)) >= 0xdc00 &&   \
       (*(IMUInt16*)((p)+1)) < 0xe000) ?    \
      2 : 1) : 0)

#define im_utf16_next_char(p)   (((IMUInt16*)(p)) + im_utf16_char_length(p))

/**
 * @brief Get an unicode char from an utf8 char sequence.
 */
IMUniChar       im_utf16_get_char           (const IMUInt16         *src);

/**
 * @brief Return how many unicode chars in a given utf16 sequence.
 *
 * The im_utf16_strlen() function shall compute the number of characters at most maxlen bytes in the utf16 string
 * to src points, not including the terminating null byte.
 *
 * If src is longer than maxlen bytes, and maxlen has positive value, only first maxlen bytes are used to compute the number of
 * characters.
 * If maxlen indicates -1, whole utf16 string to src points are computed.
 *
 * In the case of src doesn't points utf16 string, the behavior is unspecified.
 *
 * @param src The utf16 sequence to be examined.
 * @param maxlen Maximum length of src, -1 means until 0.
 * @returns The im_utf16_strlen() function shall return the length of s.
 */
IMSize          im_utf16_strlen             (const IMUInt16         *src,
                                             IMInt                   maxlen);

/**
 * @brief Validate a UTF16 string, return TRUE if valid, put pointer to
 * first invalid char in **end
 *
 * The im_utf16_string_validate() function tests for any characters encoded with utf16 at most maxlen bytes in the
 * utf16  string to src points.
 *
 * If src is longer than maxlen bytes and maxlen has positive value, only first maxlen bytes are tested.
 * If maxlen indicates -1, whole string to src points are tested.
 *
 *
 * If the str contains illegal utf16 characters and end is not a null pointer, A pointer to the first illegal character is stored 
 * in the object pointed to by end.
 */
IMBool          im_utf16_string_validate    (const IMUInt16         *str,
                                             IMInt                   maxlen,
                                             const IMUInt16        **end);

/**
 * @brief Convert an utf8 char sequence to ucs4.
 *
 * The im_unichar_from_utf8() function shall convert the first character from utf8 string pointed by src,
 * into ucs4 character.
 *
 * @param dest a pointer to a variable that points to the first available byte to store ucs4 character.
 * @param src a pointer to a variable that points to the first character in the input buffer.
 * @param src_len the size of source buffer.
 *
 * @return The im_unichar_from_utf8() function shall update the variables pointed to by the dest argument 
 * to reflect the extent of the conversion and return the number of bytes of utf8 sequences consumed to perform the conversion.
 * If a sequence of input bytes  does not form a valid character in utf8 codeset, im_unichar_from_utf8() function 
 * shall return 0 and the value pointed by dest shall be IM_UNICHAR_INVALID.
 */
IMSize          im_unichar_from_utf8        (IMUniChar              *dest,
                                             const IMChar           *src,
                                             IMInt                   src_len);

/**
 * @brief Convert an ucs4 code to utf8 char sequence.
 *
 * The im_unichar_to_utf8() function shall convert one ucs4 character to utf8 string,
 * specified by dest.
 *
 * @param dest a pointer to a variable that points to the first available byte to store utf8 string.
 * @param src a ucs4 character to be converted
 * @param dest_size the size of destination buffer.
 *
 * @return The im_unichar_to_utf8() function shall update the variables pointed to by the dest argument
 * to reflect the extent of the conversion and return the number of bytes are written to the variables pointed to by the dest argument.
 *
 */
IMSize          im_unichar_to_utf8          (IMChar                 *dest,
                                             IMUniChar               src,
                                             IMSize                  dest_size);

/**
 * @brief Convert an utf16 char sequence to ucs4.
 *
 * The im_unichar_from_utf16() function shall convert the first character from utf16 string pointed by src,
 * into ucs4 character.
 *
 * @param dest a pointer to a variable that points to the first available byte to store ucs4 character.
 * @param src a pointer to a variable that points to the first character in the input buffer.
 * @param src_len the size of source buffer.
 *
 * @return number of uint16 elements were actually converted.
 */ 
IMSize          im_unichar_from_utf16       (IMUniChar              *dest,
                                             const IMUInt16         *src,
                                             IMInt                   src_len);

/**
 * @brief Convert an ucs4 code to utf16 char sequence.
 *
 * @param dest destination buffer to store utf16 char sequence.
 * @param src the ucs4 code to be converted.
 * @param dest_size the size of destination buffer.
 *
 * @return the number of uint16 elements actually written into dest.
 */
IMSize          im_unichar_to_utf16         (IMUInt16               *dest,
                                             IMUniChar               src,
                                             IMSize                  dest_size);

/**
 * @brief Validate an unicode character
 * */
IMBool          im_unichar_validate         (IMUniChar               ch);

/**
 * @brief Convert an utf8 string to ucs4 string.
 *
 * @param dest Buffer to store result ucs4 string.
 * @param dest_size Size of the dest buffer.
 * @param src Source utf8 string to be converted.
 * @param src_len Length of source utf8 string, in number of bytes,
 *                -1 means until 0.
 * @return How many unicode chars actually converted, if it's less than
 *         dest_size, then dest will be terminated by 0.
 */
IMSize          im_utf8_string_to_ucs4      (IMUniChar              *dest,
                                             IMSize                  dest_size,
                                             const IMChar           *src,
                                             IMInt                   src_len);

/**
 * @brief Convert an utf16 string to ucs4 string.
 *
 * @param dest Buffer to store result ucs4 string.
 * @param dest_size Size of the dest buffer.
 * @param src Source utf8 string to be converted.
 * @param src_len Length of source utf8 string, in number of uint16 elements,
 *                -1 means until 0.
 * @return How many unicode chars actually converted, if it's less than
 *         dest_size, then dest will be terminated by 0.
 */
IMSize          im_utf16_string_to_ucs4     (IMUniChar              *dest,
                                             IMSize                  dest_size,
                                             const IMUInt16         *src,
                                             IMInt                   src_len);

/**
 * @brief Return how many unicode chars in a given ucs4 sequence.
 *
 * The im_utf8_strlen() function shall compute the number of characters in the string
 * to src points, not including the terminating null byte.
 *
 * @param src The zero terminated ucs4 sequence to be examined.
 * @param maxlen Maximum length of src, -1 means until 0.
 */
IMSize          im_ucs4_strlen              (const IMUniChar        *src,
                                             IMInt                   maxlen);

/**
 * @brief Return the minimum dest buffer size when converting an ucs4 string
 *        to utf8 string.
 */
IMSize          im_ucs4_string_utf8_length  (const IMUniChar        *str,
                                             IMInt                   maxlen);

/**
 * @brief Return the minimum dest buffer size when converting an ucs4 string
 *        to utf16 string.
 */
IMSize          im_ucs4_string_utf16_length (const IMUniChar        *str,
                                             IMInt                   maxlen);

/**
 * @brief Convert an ucs4 string to utf8 string.
 *
 * @param dest Buffer to store result utf8 string.
 * @param dest_size Size of the dest buffer, in bytes.
 * @param src Source ucs4 string to be converted.
 * @param src_len Length of source ucs4 string, in number of IMUniChar elements,
 *                -1 means until 0.
 * @return How many bytes actually written to dest, if it's less than
 *         dest_size, then dest will be terminated by 0.
 */
IMSize          im_ucs4_string_to_utf8      (IMChar                 *dest,
                                             IMSize                  dest_size,
                                             const IMUniChar        *src,
                                             IMInt                   src_len);

/**
 * @brief Convert an ucs4 string to utf16 string.
 *
 * @param dest Buffer to store result utf16 string.
 * @param dest_size Size of the dest buffer, in bytes.
 * @param src Source ucs4 string to be converted.
 * @param src_len Length of source ucs4 string, in number of IMUniChar elements,
 *                -1 means until 0.
 * @return How many elements actually written to dest, if it's less than
 *         dest_size, then dest will be terminated by 0.
 */
IMSize          im_ucs4_string_to_utf16     (IMUInt16               *dest,
                                             IMSize                  dest_size,
                                             const IMUniChar        *src,
                                             IMInt                   src_len);
IM_END_DECLS
/** @} */

#endif
/*
vi:ts=4:nowrap:ai:expandtab
*/
